/*******************************************************************************
* Copyright (C) 2014 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*******************************************************************************
*/
/*============================================================================
 * 
 * Author(s): Maxim Integrated Products
 * Contributors:
 * Date:          
 * Purpose:       
 * Description:
 * 
 *==========================================================================*/
#include <string.h>
#include <errors.h>

#include <mml_sc.h>
#include <mml_sc_dev.h>
#include <libemv.h>

#include <emv_config.h>
#include <emv_slot.h>
#include <emv_timer.h>


/** _DBG_YG_ */
/* 42000 Smart Card clock cycles = 115 etus
 * 42000 / 372 = 112.903226 */
/*#define EMV_42000_CYCLES				((unsigned int) 42780 / (unsigned int) 372)*/
#define EMV_42000_CYCLES				42000
/*  */
/* + 1 is for propagation through SC chipset and
 * code execution latency - empirical */
#define EMV_INITIAL_WT                  10190 //HA debug emv (10080 + 1)
//#define EMV_INITIAL_WT					10080
/*  */

#define STATE_ATR_PARSE					0
#define STATE_ATR_T1_IFSD				1
#define STATE_ATR_PPS					2
#define STATE_ATR_DONE					3

/* Derived modification due to bug fix #1034 */
#define	emv_set_bwt(_b_)			(mml_sc_ioctl( N_SCS_IOCTL_SET_BWT, _b_))
#define	emv_set_cwt(_b_)			(mml_sc_ioctl( N_SCS_IOCTL_SET_CWT, _b_))
/*  */

extern const unsigned char emv_Di_tab[16];

int emv_pps_send( emv_slot_t *slot, unsigned char TA1, unsigned char protocol );
int emv_pps_check( emv_slot_t *slot );
void emv_power_up_handler( int event );
void emv_power_up_pps_handle(void);
void emv_power_up_ifsd_handle( emv_slot_t *slot );
int hal_scs_prepare_warmreset(void);


int emv_power_up(unsigned char *atr, unsigned int *length )
{
    int					result;
    emv_slot_t			*slot = emv_get_slot();
//    int ha_toggle;

    /** Check the slot identifier */
    if ( (unsigned int)slot == EMV_FAKE_ADD_PATTERN )
    {
        result = COMMON_ERR_NO_DEV;
        goto emv_power_up_out;
    }
	/* Bug correction #1037 */
	/* Check if the slot is initialized */  
    else if ( !( slot->status & EMV_STATE_INITIALIZED ) )
    {  
        result = N_SCS_ERR_DEV_NO_SUPP;
        goto emv_power_up_out;
    }
    /* Check if an operation is already pending */  
    else if ( slot->status & EMV_STATE_OPERATION_PENDING )
    {
        result =  COMMON_ERR_ALREADY;
        goto emv_power_up_out;
    }
    /* Check parameters */
    else if ( !atr ||
			!length )
	{
        result =  COMMON_ERR_INVAL;
        goto emv_power_up_out;
	}
    /* register the handler to manage ATR events */ 
    mml_sc_attach(MML_SC_EVENT_ATR_TS_RECEIVED, emv_power_up_handler);
    mml_sc_attach( MML_SC_EVENT_ATR_RECEIVED, emv_power_up_handler);
    mml_sc_attach( MML_SC_EVENT_ERROR, emv_power_up_handler);
    mml_sc_attach( MML_SC_EVENT_DATA_RECEIVED, emv_power_up_handler);
    /* Initialize status */
    slot->status = EMV_STATE_INITIALIZED;
    slot->status &= ~EMV_STATE_MODE_SPECIFIC;
    slot->status &= ~EMV_STATE_PPS_PENDING;
    slot->status |= EMV_STATE_COLD_RESET;
    slot->status |= EMV_STATE_OPERATION_PENDING;
    slot->error = NO_ERROR;
    /* Initialize buffer */
    slot->r_apdu.data = atr;
    slot->r_apdu.length = length;
    slot->r_apdu.alloc = *length;
    *slot->r_apdu.length = 0;
    slot->rx_buf.length = EMV_CONFIG_APDU_MAX_SIZE;
    slot->state = STATE_ATR_PARSE;
	/* Derived modification due to bug fix #1034 */
	/* Maximum clock cycles for ATR to begin */
	slot->bwt = EMV_42000_CYCLES;
	emv_set_bwt((void *) slot->bwt);
	/* for EMV: intra character period is max 9600 + 480 etus for ATR */
	slot->cwt = EMV_INITIAL_WT;
	emv_set_cwt((void *) slot->cwt);
    /* Cold reset */
    result = mml_sc_power_up( slot->rx_buf.data, &slot->rx_buf.length);
    if ( result )
    {
    	slot->status &= ~(EMV_STATE_OPERATION_PENDING |
    						EMV_STATE_PPS_PENDING |
    						EMV_STATE_IFSD_PENDING);
    	slot->error = result;
    	/* Bug correction #752 */
    	goto emv_power_up_out;
    	/*  */
    }
    /* Blocking function if no interrupt handler */
    if ( !slot->handlers[EMV_EVENT_ATR_COMPLETED] )
    {
    	while((slot->status & EMV_STATE_OPERATION_PENDING) && ((slot->status & EMV_STATE_DO_WARM_RESET)==0));

    	if(slot->status & EMV_STATE_DO_WARM_RESET)
    	{
    		slot->rx_buf.length = EMV_CONFIG_APDU_MAX_SIZE;
    		/* Maximum clock cycles for ATR to begin */
    		/* Derived modification due to bug fix #1034 */
    		/* Maximum clock cycles for ATR to begin */
    		slot->bwt = EMV_42000_CYCLES;
    		emv_set_bwt( (void *) slot->bwt);
    		/* for EMV: intra character period is max 9600 + 480 etus for ATR */
    		slot->cwt = EMV_INITIAL_WT;
    		emv_set_cwt((void *) slot->cwt);
    		slot->error = mml_sc_power_up(slot->rx_buf.data, &slot->rx_buf.length);

    		while(slot->status & EMV_STATE_OPERATION_PENDING);
    	}
    	slot->status &=(~EMV_STATE_OPERATION_PENDING);
		/* Bug correction #1033 */
		/* Assuming that if no handler has been provided by
		 * EMV's user, PPS and IFSD negotiations are handled
		 * automatically.
		 */
		/* PPS negotiation ? */
		emv_power_up_pps_handle();
		/* IFSD negotiation ? */
		emv_power_up_ifsd_handle(slot);
    }
    /* Bug correction #1033 */
	/* If EMV's client has provided an handler for
	 * EMV_EVENT_ATR_COMPLETED, it's upto it to
	 * launch PPS and/or IFSD negotiation(s).
	 */
	result = slot->error;
emv_power_up_out:
	return result;
}

void emv_power_up_handler(int event )
{
	int						result;
    emv_slot_t          	*slot = emv_get_slot();
    emv_buf_t           	*rx_buf = &slot->rx_buf;
    emv_apdu_t          	*r_apdu = &slot->r_apdu;

    switch( event )
    {
        case MML_SC_EVENT_ATR_TS_RECEIVED:
            /* Start timer */
            result = emv_timer_run(slot);
            if ( result )
            {
            	slot->error = N_SCS_ERR_UNKNOW;
            }
            else
            {
            	break;
            }
        case MML_SC_EVENT_ERROR:
			/* Retrieve last error */
            mml_sc_get_last_error((int *) &slot->error);
            if ( ( slot->error != N_SCS_ERR_UNKNOW )
                && ( slot->state != STATE_ATR_T1_IFSD ) )
            {
                /* Stop timer */
                emv_timer_halt(slot);
                emv_power_down();
                slot->status &= ~(EMV_STATE_OPERATION_PENDING |
                					EMV_STATE_PPS_PENDING |
                					EMV_STATE_IFSD_PENDING);
				slot->ApplicableValues = HAL_SCS_APPLY_NONE;
                emv_call_handler(slot, EMV_EVENT_ERROR);
                goto emv_power_up_handler_out;   
            }
            else if ( slot->error == N_SCS_ERR_CMD_ABORTED )
            {
                slot->status &= ~(EMV_STATE_OPERATION_PENDING |
                					EMV_STATE_PPS_PENDING |
                					EMV_STATE_IFSD_PENDING);
                slot->ApplicableValues = HAL_SCS_APPLY_NONE;
                emv_call_handler(slot, EMV_EVENT_ERROR);
                goto emv_power_up_handler_out;
            }
            else if ( ( slot->error ) &&
            		( slot->state == STATE_ATR_T1_IFSD ) )
            {
            	/* continue to case HAL_SCS_EVENT_DATA_RECEIVED */
            	goto there;
            }
            else
            {
				break;
            }
        case MML_SC_EVENT_ATR_RECEIVED:
			/* Stop timer */
			emv_timer_halt(slot);
			/* Parse the received ATR */
			slot->error = emv_atr_parse(slot, (emv_atr_t*) &(slot->atr_info));
			if ( slot->status & EMV_STATE_COLD_RESET )
			{
				slot->status &= ~EMV_STATE_COLD_RESET;
				/* Prepare warm reset and return */
				if ( ( slot->error == N_SCS_ERR_UNKNOW ) ||
					( ( slot->error == NO_ERROR ) &&
					( slot->atr_info.emv_flag == 0 ) ) )
				{
					slot->status |=EMV_STATE_DO_WARM_RESET;
					return;
				}
			}

			if ( slot->error == NO_ERROR )
			{
				if ( !slot->atr_info.emv_flag )
				{
					slot->error = N_SCS_ERR_INVALID_ATR;
				}
				else
				{
					slot->ApplicableValues = HAL_SCS_APPLY_ALL;
					/* Update the receive buffer */
					if ( rx_buf->length <= r_apdu->alloc )
					{
						emv_memcpy(r_apdu->data, rx_buf->data, rx_buf->length);
						*(r_apdu->length) = rx_buf->length;
					}
					else
						/* buffer overflow */
						slot->error = N_SCS_ERR_NO_BUFS;
					/* PPS negotiation or specific mode */
					if ( ( slot->mode & EMV_MODE_MANAGE_PPS ) &&
						( slot->atr_info.fd != 0x11 ) &&
						!( slot->status & EMV_STATE_MODE_SPECIFIC ) )
					{
						slot->state = STATE_ATR_PPS;
						slot->status |= EMV_STATE_PPS_PENDING;
						slot->status &= ~EMV_STATE_OPERATION_PENDING;
						slot->ApplicableValues = HAL_SCS_APPLY_NONE;
						goto emv_power_up_handler_out;
					}
					else if ( !( slot->mode & EMV_MODE_MANAGE_PPS ) &&
							/* Bug correction #742 */
							!( slot->status & EMV_STATE_MODE_SPECIFIC ) )
					{
						/* out of EMV standard so keep FD = 0x11 */
						slot->ApplicableValues = HAL_SCS_APPLY_OTHER_ATR_VALUES;
					}
					/* ... */
					slot->state = STATE_ATR_DONE;
				}
			}
			else
			{
				slot->error = N_SCS_ERR_INVALID_ATR;
			}
            break;     
        case MML_SC_EVENT_DATA_RECEIVED:
there:          switch( slot->state )
                {
                    case STATE_ATR_PPS:
                        /* No check for now */
						if ( emv_pps_check(slot) )
						{
							slot->ApplicableValues = HAL_SCS_APPLY_NONE;
							slot->status &= ~EMV_STATE_IFSD_PENDING;
							/* Stop timer */
							emv_timer_halt(slot);
							emv_power_down();
							emv_call_handler(slot, EMV_EVENT_ERROR);
						}
						else
						{
							slot->ApplicableValues = HAL_SCS_APPLY_ALL;
						}

						slot->state = STATE_ATR_DONE;
						slot->status &= ~EMV_STATE_PPS_PENDING;
                        goto emv_power_up_handler_out;
                    case STATE_ATR_T1_IFSD:
						/* Check previous IFS response */
						if ( slot->error == NO_ERROR )
						{
							/* Check NAD, PCB, LEN, INFO and LRC */                           
							if ( ( emv_t1_check_block(slot) == NO_ERROR )
								&& (rx_buf->length == 5)
								&& (rx_buf->data[1] == IFS_RSP) 
								&& (rx_buf->data[3] == CONFIG_EMV_T1_IFSD) )
							{
								slot->state = STATE_ATR_DONE;
								slot->status &= ~EMV_STATE_IFSD_PENDING;
								goto emv_power_up_handler_out;
							}
							else
							{
								slot->error = N_SCS_ERR_UNKNOW;
							}
						}
						else if ( ( slot->error == N_SCS_ERR_CARD_ABSENT ) ||
								( !( slot->mode & EMV_MODE_T1_MANAGE_TIMEOUT ) &&
								( slot->error == N_SCS_ERR_CARD_MUTE ) ) )
						{
							slot->retries = 0;
						}
						else
						{
							slot->error = N_SCS_ERR_UNKNOW;
						}

						if ( slot->retries-- )
						{
							unsigned char ifsd = CONFIG_EMV_T1_IFSD;
							slot->error = emv_t1_block(slot, IFS_REQ, &ifsd, 1);
							goto emv_power_up_handler_out;           
						} 
						else
						{ 
							slot->error = N_SCS_ERR_UNKNOW;
							//HA UL lab cession 2
						delay_micro(CONFIG_PATH_WAIT_LAB_UL);  //Add delay before desactivation fix 1813 OK

						}

						break;
					default:
						slot->error = N_SCS_ERR_UNKNOW;
	                    break;
                }
                break;
		default:
			slot->error = N_SCS_ERR_UNKNOW;
			break;
    }
	/**  */
    if ( slot->error )
    {
        slot->status &= ~EMV_STATE_OPERATION_PENDING;
		slot->status &= ~EMV_STATE_PPS_PENDING;
		slot->status &= ~EMV_STATE_IFSD_PENDING;
		slot->ApplicableValues = HAL_SCS_APPLY_NONE;
        emv_call_handler(slot, EMV_EVENT_ERROR);
    }
    else if ( slot->state == STATE_ATR_DONE )
    {
		slot->status &= ~EMV_STATE_OPERATION_PENDING;
		slot->status &= ~EMV_STATE_PPS_PENDING;
		/* Bug correction #1033 */
    	if ( slot->handlers[EMV_EVENT_ATR_COMPLETED] ) 
		{
			emv_call_handler(slot, EMV_EVENT_ATR_COMPLETED);
		}
    }   

emv_power_up_handler_out:
	return;
}

int emv_pps_send(emv_slot_t *slot, unsigned char TA1, unsigned char protocol)
{
	register unsigned char i;

    /* Check if we are in negociate mode */
	slot->tx_frame[0] = 0xFF ;
	slot->tx_frame[1] = 0x10 + protocol ;
	slot->tx_frame[2] = TA1 ;
	slot->tx_frame[3] = 0xFF ^ slot->tx_frame[1] ;
	slot->tx_frame[3] ^= TA1 ;
	slot->rx_length = 4;
	/* PTS response max size is 6 PTSS PTS0 PTS1 PTS2 PTS3 PCK */
	for ( i = 6;i > 0;)
	{
		i--;
		slot->rx_frame[i] = 0;
	}
	return mml_sc_pps_exchange(slot->tx_frame,
                                4, 
                                slot->rx_frame,
                                &slot->rx_length);
}

int emv_pps_check(emv_slot_t *slot)
{
	int						result = NO_ERROR;
	register char			i = 0;

	if ( slot->rx_frame[0] != 0xFF )
	{
		slot->error = N_SCS_ERR_UNKNOW;
		result = N_SCS_ERR_UNKNOW;
		goto emv_pps_check_out;
	}	
	else if ( memcmp(slot->tx_frame, slot->rx_frame, 4) )
	{
		unsigned char							PCK;
		/* response not echoed thus analyze it */
		/* check length */
		if ( slot->rx_length < 3 )
		{
			slot->error = N_SCS_ERR_UNKNOW;
			result = N_SCS_ERR_UNKNOW;
			goto emv_pps_check_out;
		}
		else PCK = slot->rx_frame[0];

		if ( ( slot->rx_frame[1] & 0x10 ) == 0 )
		{
			/* Fi&Di's default values has to be used */
			slot->atr_info.fi = 1;
			slot->atr_info.di = 1;
			slot->atr_info.fd = 0x11;
		}
		/* check protocol range */
		if ( ( slot->rx_frame[1] & 0x0F ) > EMV_PROTOCOL_T1 )
		{
			slot->error = N_SCS_ERR_UNKNOW;
			result = N_SCS_ERR_UNKNOW;
			goto emv_pps_check_out;
		}

		if ( slot->atr_info.protocol != ( slot->rx_frame[1] & 0x0F ) )
			slot->atr_info.protocol = slot->rx_frame[1] & 0x0F;

		for ( i = (slot->rx_length - 1);i > 0;)
		{
			i--;
			PCK ^= slot->rx_frame[i];
		}

		if ( PCK != slot->rx_frame[slot->rx_length - 1] )
		{
			slot->error = N_SCS_ERR_UNKNOW;
			result =  N_SCS_ERR_UNKNOW;
		}
	}

emv_pps_check_out:
    return result;
}

void emv_power_up_pps_handle()
{
	emv_slot_t				*slot = emv_get_slot();
	mml_sc_slot_info_t		slot_info;

	if ( ( slot->mode & EMV_MODE_MANAGE_PPS ) &&
		!slot->error )
	{
		/* process unlocked then PPS request send if negotiable mode is active */
		if ( slot->status & EMV_STATE_PPS_PENDING )
		{
			/* we can do stuff */
			slot->error = emv_pps_send(slot, (unsigned char)slot->atr_info.fd, (unsigned char)slot->atr_info.protocol);
			if ( slot->error )
			{
				slot->status &= ~EMV_STATE_IFSD_PENDING;
		        emv_power_down();
		        emv_call_handler(slot, EMV_EVENT_ERROR);
		        return;
			}
		}
		else
		{
			/* Derived modification due to bug fix #1138 */
			slot->ApplicableValues = HAL_SCS_APPLY_ALL;
			/*  */
		}
		/* Wait for ATR received and analyzed in order to initiate
		 * PPS exchange */
		while( slot->status & EMV_STATE_PPS_PENDING );
	}
	/* get back previous value from HAL_SCS layer abd update with new ones */
	mml_sc_get_slot_info(&slot_info);
	/* applicate values received in the ATR */
	slot_info.FIDI = emv_set_FD_values(slot, (emv_atr_t *) &(slot->atr_info), slot->ApplicableValues);
	/* application of other ATR values */
	if ( slot->ApplicableValues & HAL_SCS_APPLY_OTHER_ATR_VALUES )
	{
		slot->protocol = slot->atr_info.protocol;
		slot_info.protocol = slot->atr_info.protocol;
		slot->n = slot->atr_info.n;
		slot_info.EGT = slot->atr_info.n;
		slot_info.WI = slot->atr_info.wi;
	}

	if ( slot->ApplicableValues )
	{
		emv_calculate_timing(slot, (emv_atr_t *) &(slot->atr_info));
		/* Derived modification due to bug fix #1034 */
		emv_set_cwt((void *) slot->cwt);
		emv_set_bwt((void *) slot->bwt);
	}

	mml_sc_set_slot_info( &slot_info);
	return;	
}

void emv_power_up_ifsd_handle( emv_slot_t *slot )
{
	unsigned char ifsd = CONFIG_EMV_T1_IFSD;

	if ( ( slot->mode & EMV_MODE_T1_IFSD ) &&
		( slot->status & EMV_STATE_IFSD_PENDING ) &&
		( slot->protocol == EMV_PROTOCOL_T1 ) )
	{
		/* Bug correction #777 */
		slot->retries = CONFIG_EMV_T1_MAX_RETRIES;
		/*  */
		slot->state = STATE_ATR_T1_IFSD;
		slot->error = emv_t1_block(slot, IFS_REQ, &ifsd, 1);
		/* Bug correction #776 */
		if ( slot->error )
		{
			slot->status &= ~EMV_STATE_IFSD_PENDING;
		}
		/*  */
	}
	else
	{
		slot->status &= ~EMV_STATE_IFSD_PENDING;
	}

	while( slot->status & EMV_STATE_IFSD_PENDING );
	return;
}
